package cflow;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.LinkedList;

import logical.Automata;
import logical.AutomataGenerator;
import logical.DFA;
import logical.SetState;
import logical.SimpleNode;
import logical.TransitionDFA;

public class Cflow {

	public static int HALT = 0;
	public static int CONTINUE = 1;
	public static boolean GRAPH = true;
	public static boolean NOGRAPH = false;
	private boolean running = true;
	private int mode = 1;
	private int delayTime = 1000;
	private DFA dfa;
	private SetState currentState;
	private boolean showGraph = true;
	private boolean debugInfo = true;

	private LinkedList<String> inputTrace = new LinkedList<String>();
	private LinkedList<String> acceptedTrace = new LinkedList<String>();

	public SetState getCurrentState()
	{
		return currentState; 
	}
	public static void main(String args[]){
		System.out.println("Reading from standard input...");
		System.out.print("Enter a regular expression: ");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String s;
		try {
			Cflow c;
			if ((s = in.readLine()) != null && s.length() != 0) {
				c = new Cflow(s);
				if(!c.running){
					return;
				}
				c.setDebugInfo(false);
				System.out
						.println("Enter the symbols to use in the graph. Close the graph window to exit.");
				do {
					s = in.readLine();
					if (s != null) {
						if (s.length() != 0) {
							c.addTransition(s);
						}
					}
				} while (true);
			}
		} catch (IOException e) {
			System.out.println("Invalid regex supplied.");
			System.exit(1);
		}
	}

	public Cflow(String regex) {
		regex = regex + '\n';
		init(regex);
	}

	public Cflow(String regex, int mode) {
		regex = regex + '\n';
		this.mode = mode;
		init(regex);
	}

	public Cflow(String regex, int mode, int delay) {
		regex = regex + '\n';
		this.mode = mode;
		delayTime = delay;
		init(regex);
	}

	public Cflow(String regex, int mode, int delay, boolean graph) {
		regex = regex + '\n';
		this.mode = mode;
		delayTime = delay;
		showGraph = graph;
		init(regex);
	}

	private void init(String regex) {
		String name = new String(regex);
		InputStream input = new ByteArrayInputStream(regex.getBytes());
		try {
			AutomataGenerator automataGen = new AutomataGenerator(input);
			try {
				SimpleNode n = automataGen.Start();
				Automata automata = n.createAutomata("");
				dfa = new DFA(automata, this);

				dfa.createVisualization(showGraph);

				currentState = dfa.getInitialState();
				if(showGraph){
					dfa.setName(name);
				}
				Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
					public void run() {
						System.out.println("Execution stopped.");
						printlog();
					}
				}));
			} catch (Exception e) {
				System.err
						.println("Invalid expression detected. Received the following expression:\n"
								+ name
								+ "\nCflow will ignore all further actions.");
				running = false;
				return;
			}
		} catch (Error e) {
			running = false;
			System.out.println("Invalid regex supplied.");
		}

	}

	public void printlog() {
		String s = "";
		if (currentState.isFinal()) {
			s += "Reached a final state.\n\n";
		} else {
			s += "Failed to reach a final state.\n\n";
		}
		if (!inputTrace.isEmpty()) {
			s += "Received the following input sequence:\n";
			for (String value : inputTrace) {
				s += value + "->";
			}
			s = s.substring(0, s.length() - 2);
			System.out.println(s + "\n");
		}
		if (!acceptedTrace.isEmpty()) {
			s = "Accepted the following input sequence:\n";
			for (String values : acceptedTrace) {
				s += values + "->";
			}
			s = s.substring(0, s.length() - 2);
			System.out.println(s+"\n");
		}
	}

	public void addTransition(String input) {
		if (!running) {
			return;
		}
		inputTrace.add(input);
		if (!transition(input)) {
			Exception e = new Exception();
			System.err.println("Invalid transition detected");
			if (debugInfo) {
				System.err.println("Faulty transition called in "
						+ e.getStackTrace()[1].toString()
						+ "\nWill continue recording transitions.");
			}
		} else {
			acceptedTrace.add(input);
		}
	}

	private boolean transition(String input) {
		for (TransitionDFA df : currentState.getTransitions()) {
			if (df.getValue().equals(input)) {
				currentState = df.getDestinationState();
				if (showGraph) {
					dfa.refresh(currentState);
				}
				if (mode == HALT) {
					try {
						Thread.sleep(delayTime);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				return true;
			}
		}
		return false;
	}

	public LinkedList<String> getAcceptedTrace() {
		return acceptedTrace;
	}

	private void setDebugInfo(boolean debug) {
		debugInfo = debug;
	}
}
